﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Project1
{
    class Unit
    {
        public Texture2D sprite;
        public Vector2 pos;
        public Vector2 vel;
        public bool alive;

        public Unit( Texture2D texture )
        {
            sprite = texture;
            pos = vel = Vector2.Zero;
            alive = false;
        }

        public virtual void update(double elapsedTime)
        {
            pos += vel * (float)elapsedTime;
        }

        public virtual void draw(SpriteBatch sb)
        {
            if( alive )
                sb.Draw(sprite, pos, new Rectangle(0, 0, sprite.Width, sprite.Height), Color.White);
        }

        public virtual bool collides(Unit that)
        {
            if (!this.alive || !that.alive)
                return false;
            Rectangle thisBox = new Rectangle((int)pos.X - sprite.Width / 2, (int)pos.Y - sprite.Height / 2, sprite.Width, sprite.Height);
            Rectangle thatBox = new Rectangle((int)that.pos.X - that.sprite.Width / 2, (int)that.pos.Y - that.sprite.Height / 2, that.sprite.Width, that.sprite.Height);
            return thisBox.Intersects(thatBox);
        }
    }

    class PlayerShip : Unit
    {   
        // secondary weapon - the powerup
        // 0 = None
        // 1 = Spread Shot
        // 2 = Rapid Fire
        // 3 = Triple Shot
        // Primary Weapon - regular fire
        public int primaryWeapon; //if primaryWeapon = 0, fire primary, if 1, fire secondary
        public int secondaryWeapon;
        public int[][] animateSequence;
        private Rectangle[] animateRect;
        private double animateTimer;
        public List<Bullet> bullet_container;
        public int state;
        public int bullet_timer;
        public Texture2D bullet_texture;
        public int frame;

        public PlayerShip( Texture2D texture, Texture2D b_texture ): base( texture )
        {
            primaryWeapon = secondaryWeapon = 0;
            // This can be changed if you know a better way to do animations
            // Each number in the sequence will basically correspond to a different box on the sprite sheet representing the player ship
            // And update will basically go through each animation sequence depending on what state the ship is in.
            animateRect = new Rectangle[3];
            for (int i = 0; i < 3; i++)
                animateRect[i] = new Rectangle(0, 16 * i, 32, 16);
            animateSequence = new int[5][];
            animateSequence[0] = new int[] { 0, 1, 2 };               // Neutral sequence
            animateSequence[1] = new int[] { 0, 1, 2, 3 };      // Flying up/down sequence
            animateSequence[2] = new int[] { 0, 1, 2, 3 };      // Flying forwards/backwards sequence
            animateSequence[3] = new int[] { 0, 1, 2, 3 };      // Firing sequence
            animateSequence[4] = new int[] { 1, 2, 3, 4 };      // Death sequence
            animateTimer = 0.0;
            state = frame = 0;
            //initialize txt
            sprite = texture;
            pos.X = 10;
            pos.Y = 100;
            vel.X = 2;
            vel.Y = 2;
            bullet_timer = 0;
            bullet_texture = b_texture;
            bullet_container = new List<Bullet>();
            alive = true;


        }

        public override void update(double elapsedTime)
        {
            animateTimer -= elapsedTime;
            if (animateTimer <= 0)
            {
                frame = (frame + 1) % (animateSequence[state].Length);
                animateTimer += 0.25;
            }
            //get state of keyboard
            KeyboardState keyState = Keyboard.GetState();
            
            //move ship up and down
            if (keyState.IsKeyDown(Keys.Up))
            {
                if (pos.Y > 0)
                    pos.Y -= vel.Y;
            }
            if (keyState.IsKeyDown(Keys.Down))
                if (pos.Y < GlobalState.Screen_Height - +sprite.Height)
                    pos.Y += vel.Y;

            if (keyState.IsKeyDown(Keys.Right))
            {
                if (pos.X < 200)  //at the moment just an arbitrary number.
                    pos.X += vel.X;
            }
            if (keyState.IsKeyDown(Keys.Left))
            {
                if (pos.X > 0)
                    pos.X -= vel.X;
            }
            if (keyState.IsKeyDown(Keys.F))
            {
                if (primaryWeapon == 0)
                    primaryWeapon = 1;
                else
                    primaryWeapon = 0;
            }
            
            //do nothing
            if (keyState.IsKeyDown(Keys.Z))
            {
                if (bullet_timer > GlobalState.Bullet_Delay_Max)
                {
                    //fire bullet
                    bullet_timer = 0;
                    if (primaryWeapon == 0)
                    {
                        Bullet bullet = new Bullet(bullet_texture);
                        bullet.pos = this.pos;
                        bullet.pos.X += this.sprite.Width;
                        bullet_container.Add(bullet);

                    }
                    else
                    {
                        switch (secondaryWeapon)
                        {
                            case 0:
                                primaryWeapon = 0;
                                //fire a primary weapon shot after switching
                                Bullet bullet = new Bullet(bullet_texture);
                                bullet.pos.X += this.sprite.Width;
                                bullet.pos = this.pos;
                                bullet_container.Add(bullet);
                                break;

                            case 1: //spread shot
                                //fires upward diagonal
                                Bullet b1 = new Bullet(bullet_texture);
                                b1.pos = this.pos;
                                b1.pos.X += this.sprite.Width;
                                b1.vel.Y = -1;

                                //fires regularly
                                Bullet b2 = new Bullet(bullet_texture);
                                b2.pos = this.pos;
                                b2.pos.X += this.sprite.Width;
                                //fires downward diagonal
                                Bullet b3 = new Bullet(bullet_texture);
                                b3.pos = this.pos;
                                b3.pos.X += this.sprite.Width;
                                b3.vel.Y = 1;

                                bullet_container.Add(b1);
                                bullet_container.Add(b2);
                                bullet_container.Add(b3);
                                break;

                            case 2: //rapid fire
                                GlobalState.Bullet_Delay_Max = 10;
                                Bullet b = new Bullet(bullet_texture);
                                b.pos.X += this.sprite.Width;
                                b.pos = this.pos;
                                bullet_container.Add(b);
                                break;

                            case 3: //triple shot, as in 3 bullets vertically stacked
                                Bullet bullet1 = new Bullet(bullet_texture);
                                bullet1.pos.X = this.pos.X + this.sprite.Width;
                                bullet1.pos.Y = this.pos.Y + 15;
                                Bullet bullet2 = new Bullet(bullet_texture);
                                bullet2.pos = this.pos;
                                bullet2.pos.X += this.pos.X + 15;
                                Bullet bullet3 = new Bullet(bullet_texture);
                                bullet3.pos.X = this.pos.X + this.sprite.Width;
                                bullet3.pos.Y = this.pos.Y - 15;

                                bullet_container.Add(bullet1);
                                bullet_container.Add(bullet2);
                                bullet_container.Add(bullet3);
                                break;

                            default:
                                break;
                        }
                    }
                    
                }
                //attack
            }

            bullet_timer++;

            for (int i = 0; i < bullet_container.Count; i++)
            {
                bullet_container[i].update(elapsedTime);
                if (bullet_container[i].pos.X < 0)
                    bullet_container.RemoveAt(i);
                if (bullet_container[i].pos.Y < 0 && bullet_container[i].vel.Y < 0)
                    bullet_container.RemoveAt(i);
                if (bullet_container[i].pos.Y > 640 && bullet_container[i].vel.Y > 0)
                    bullet_container.RemoveAt(i);
                if (bullet_container[i].pos.X > 800 && bullet_container[i].vel.X > 0)
                    bullet_container.RemoveAt(i);
            }

            base.update(elapsedTime);
        }

        public override void draw(SpriteBatch sb)
        {
            for (int i = 0; i < bullet_container.Count; i++)
            {
                if (bullet_container[i].alive)
                    bullet_container[i].draw(sb);
            }
            sb.Draw(sprite, pos, animateRect[animateSequence[state][frame]], Color.White);
        }
    }

    class Enemy : Unit
    {
        public int hp;
        public int flightPath;
        public double angle;
        public List<Bullet> shots = new List<Bullet>();
        private double timer;
        public Texture2D bullet;
        public Vector2 target;

        public Enemy( Texture2D texture ) : base( texture )
        {
            hp = 1;
            flightPath = 1;
            angle = Math.PI;
            timer = 0;
        }

        public Enemy( Texture2D texture, Vector2 start) : base( texture )
        {
            hp = 1;
            flightPath = 1;
            pos = start;
            angle = Math.PI;
            timer = 0;
        }
        
        public Enemy(Texture2D texture, Vector2 start, int flight) : base(texture)
        {
            hp = 1;
            flightPath = flight;
            pos = start;
            angle = Math.PI;
            timer = 0;
        }

        public Enemy(Texture2D texture, Vector2 start, int flight, int health) : base(texture)
        {
            hp = health;
            flightPath = flight;
            pos = start;
            angle = Math.PI;
            timer = 0;
        }

        public override void update(double elapsedTime)
        {
            timer -= elapsedTime;
            switch (flightPath)
            {
                case 1:
                    vel.X = -100.0f;
                    if (timer <= 0.0)
                    {
                        shoot();
                        timer += 2.0;
                    }
                    break;
                case 2:
                    vel.X = -125.0f;
                    if (pos.X < 320.0f)
                    {
                        vel.X = 0.0f;
                        vel.Y = 75.0f;
                        angle = Math.PI * 1.5;
                        if (timer <= 0.0){
                            shoot();
                            timer += 1.5;
                        }
                    }
                    break;
                case 3:
                    vel = target - pos;
                    vel.Normalize();
                    vel *= 100.0f;
                    angle = Math.Atan((double)(vel.Y / vel.X));
                    flightPath = 4;
                    break;
                case 4:
                    if (timer <= 0.0)
                    {
                        shoot();
                        timer += 5.0;
                    }
                    break;
                default:
                    break;
            }
            for (int i = 0; i < shots.Count; i++)
            {
                shots[i].update(elapsedTime);
            }
            base.update(elapsedTime);
        }

        public override void draw(SpriteBatch sb)
        {
            if (alive)
                sb.Draw(sprite, pos, new Rectangle(0, 0, sprite.Width, sprite.Height), 
                        Color.White, (float)angle, Vector2.Zero, 1.0f, SpriteEffects.None, 0);
            for (int i = 0; i < shots.Count; i++)
            {
                shots[i].draw(sb);
            }
        }

        public void shoot()
        {
            Bullet tmp = new Bullet(bullet);
            tmp.pos = this.pos - new Vector2( (float)sprite.Width/2.0f, (float)sprite.Height/2.0f );
            if (flightPath != 2)
                tmp.vel = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
            else
                tmp.vel = new Vector2((float)Math.Cos(angle - Math.PI / 2), (float)Math.Sin(angle - Math.PI / 2));
            tmp.vel *= 150.0f;
            shots.Add(tmp);
        }
    }

    class Boss : Enemy
    {
        public Boss(Texture2D texture, Vector2 start, int flight, int health) : base(texture, start, flight, health) { }
        public override void update(double elapsedTime)
        {
            switch (flightPath)
            {
                default:
                    break;
            }
            base.update(elapsedTime);
        }
    }

    class Bullet : Unit
    {
        public int damage;
        public bool bounces;

        public Bullet(Texture2D texture) : base(texture)
        {
            damage = 1;
            bounces = false;
            alive = true;
            vel.X = 100.0f;
            vel.Y = 0;
        }

        public Bullet(Texture2D texture, int dmg, bool reflect) : base(texture)
        {
            damage = dmg;
            bounces = reflect;
            alive = true;
            vel.X = 75.0f;
            vel.Y = 0;
        }

        public Bullet(Texture2D texture, int dmg, bool reflect, Vector2 fire) : base(texture)
        {
            damage = dmg;
            bounces = reflect;
            vel = fire;
            alive = true;
        }

        public override void update(double elapsedTime)
        {
            if (!alive)
                return;
            if (bounces)
            {
                // Check if it collides with terrain
                // Reverse velocity if it does
            }
            //pos.Y += vel.Y;
            //pos.X += vel.X;
            base.update(elapsedTime);
        }
    }

    class Background : Unit
    {
        public Vector2 screen;

        public Background(Texture2D bgTex, Vector2 sc) : base(bgTex)
        {
            screen = sc;
        }

        public override void update(double elapsedTime)
        {
            base.update(elapsedTime);
            if (pos.X >= screen.X)
                pos.X -= screen.X;
            if (pos.X <= -screen.X / 2.0f)
                pos.X += screen.X;
            if (pos.Y >= screen.Y)
                pos.Y -= screen.Y;
            if (pos.Y <= -screen.Y / 2.0f)
                pos.Y += screen.Y;
        }

        public override void draw(SpriteBatch sb)
        {
            sb.Draw(sprite, pos, new Rectangle(0, 0, sprite.Width, sprite.Height), Color.White);
            sb.Draw(sprite, pos - new Vector2( sprite.Width, 0.0f ), new Rectangle(0, 0, sprite.Width, sprite.Height), Color.White);
            sb.Draw(sprite, pos + new Vector2( sprite.Width, 0.0f ), new Rectangle(0, 0, sprite.Width, sprite.Height), Color.White);
        }
    }

    class Powerup : Unit
    {
        public int powerup_type; //0 = none; 1 = spread shot; 2 = rapid fire; 3 = tripleshot
        public double spawn_time;

        public Powerup(Texture2D texture, int type)//constructor
            : base(texture)
        {
            sprite = texture;
            powerup_type = type;
            alive = false;
            vel.X = -6;
            vel.Y = 0;

        }
        public Powerup(Texture2D texture, int type, Vector2 position)
            : base(texture)
        {
            sprite = texture;
            powerup_type = type;
            alive = false;
            vel.X = -6;
            vel.Y = 0;
            pos = position;
        }

        public Powerup(Texture2D texture, int type, Vector2 position, double t)
            : base(texture)
        {
            spawn_time = t;
            sprite = texture;
            powerup_type = type;
            alive = false;
            vel.X = -6;
            vel.Y = 0;
            pos = position;
        }

        public override void update(double elapsedTime)
        {
            if (alive)
                pos += vel;

            //vel should be the same as the speed the background moves at
            base.update(elapsedTime);
        }
    }
}
